package com.strong.utils;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.strong.sample.table.t_test.model.TableTestVO;
import com.strong.utils.mvc.pojo.view.ReplyVO;
import com.strong.utils.mvc.pojo.view.vab.ReplyVabListVO;
import com.strong.utils.mvc.pojo.view.vab.VabList;
import lombok.SneakyThrows;

import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * JSON工具类
 *
 * @author simen
 * @date 2022/11/14
 */
public class JSON {

    /**
     * json转换工具
     */
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        // 该特性决定了当遇到未知属性（没有映射到属性，没有任何setter或者任何可以处理它的handler），是否应该抛出一个JsonMappingException异常。
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 属性为NULL 不序列化
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
    }

    /**
     * 传入一个对象转化为json字符串并写入字节输出流
     */
    @SneakyThrows
    public static void write(OutputStream outputStream, Object param) {
        OBJECT_MAPPER.writeValue(outputStream, param);
    }

    /**
     * 将对象转为map对象
     */
    @SneakyThrows
    public static Map<String, Object> toMap(Object object) {
        return OBJECT_MAPPER.convertValue(object, Map.class);
    }

    /**
     * 将对象转为json字符串
     */
    @SneakyThrows
    public static String toJSONString(Object object) {
        return OBJECT_MAPPER.writeValueAsString(object);
    }

    /**
     * 将对象转为json字符串
     */
    @SneakyThrows
    public static String toJSONString(Object object, boolean isFormat) {
        if (isFormat) {
            return OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
        } else {
            return OBJECT_MAPPER.writeValueAsString(object);
        }
    }

    /**
     * 获取json对象节点
     */
    @SneakyThrows
    public static JsonNode parseJsonNode(String content) {
        return OBJECT_MAPPER.readTree(content);
    }

    /**
     * 将Bean对象转换为另外一个Bean对象
     */
    @SneakyThrows
    public static <T> T parseObject(Object fromValue, Class<T> valueType) {
        return parseObject(JSON.toJSONString(fromValue), valueType);
    }

    /**
     * 将List对象转换为另外一个List对象
     *
     * @param fromValue 从价值
     * @param valueType 值类型
     * @return {@link List }<{@link T }>
     */
    @SneakyThrows
    public static <T> List<T> parseArray(Object fromValue, Class<T> valueType) {
        return parseArray(JSON.toJSONString(fromValue), valueType);
    }

    /**
     * 将json字符串反序列化为一个Bean对象
     *
     * @param jsonString json字符串
     * @param valueType  待转换的对象类型
     * @return {@link T }
     */
    @SneakyThrows
    public static <T> T parseObject(String jsonString, Class<T> valueType) {
        return OBJECT_MAPPER.readValue(jsonString, valueType);
    }

    /**
     * 将json字符串反序列化为一个Reply对象
     *
     * @param strJson json字符串
     * @param classVO Reply泛型VO对应的类型
     * @return {@link ReplyVO }<{@link TableTestVO }>
     */
    @SneakyThrows
    public static <T> ReplyVO<T> parseReply(String strJson, Class<T> classVO) {
        Assert.notBlank(strJson, "输入的JSON文本为空");

        ReplyVO<JsonNode> replyJsonNode = JSON.parseObject(strJson, ReplyVO.class);
        Assert.notNull(replyJsonNode, "转换为的json对象为空\n{}", strJson);

        Object objData = replyJsonNode.getData();
        Assert.notNull(replyJsonNode, "ReplyVO的data为空\n{}", objData);

        if (classVO.equals(String.class)) {
            return new ReplyVO<>((T) objData, replyJsonNode.getMsg(), replyJsonNode.getCode());
        } else {
            return new ReplyVO<>(StrongUtils.toBean(objData, classVO), replyJsonNode.getMsg(), replyJsonNode.getCode());
        }
    }

    /**
     * 将json字符串反序列化为一个ReplyVabList对象
     *
     * @param strJson json字符串
     * @param classVO Reply泛型VO对应的类型
     * @return {@link ReplyVabListVO }<{@link T }>
     */
    public static <T> ReplyVabListVO<T> parseReplyVabList(String strJson, Class<T> classVO) {
        Assert.notBlank(strJson, "输入的JSON文本为空");

        ReplyVabListVO<Map<String, Object>> replyVabListJsonNode = JSON.parseObject(strJson, ReplyVabListVO.class);
        Assert.notNull(replyVabListJsonNode, "转换为的json对象为空\n{}", strJson);

        VabList<Map<String, Object>> vabListJsonNodeData = replyVabListJsonNode.getData();
        Assert.notNull(vabListJsonNodeData, "json对象的data为空\n{}", strJson);

        List<Map<String, Object>> listMap = vabListJsonNodeData.getList();

        List<T> listT = new ArrayList<>();
        if (IterUtil.isNotEmpty(listMap)) {
            for (Map<String, Object> mapNode : listMap) {
                listT.add(StrongUtils.toBean(mapNode, classVO));
            }
        }

        return new ReplyVabListVO<>(listT, vabListJsonNodeData.getTotal(),
                replyVabListJsonNode.getMsg(), replyVabListJsonNode.getCode());
    }

    /**
     * 判断字符串是否可以转为valueType的对象
     *
     * @param jsonString json字符串
     * @param valueType  待转换的对象类型
     * @return boolean
     */
    public static <T> boolean isJson(String jsonString, Class<T> valueType) {
        // 获取valueType的字段对象字段
        Field[] fields = ReflectUtil.getFields(valueType);
        Assert.isTrue(ArrayUtil.isNotEmpty(fields), "待转换对象Class的字段不存在");

        // 获取valueType的字段名数组
        String[] strsFieldName = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            strsFieldName[i] = fields[i].getName();
        }
        return isJson(jsonString, strsFieldName);
    }

    /**
     * 判断字符串是否可以转为valueType的对象
     *
     * @param jsonString    json字符串
     * @param strsFieldName 待转换的对象类型的filedName数组
     * @return boolean
     */
    public static <T> boolean isJson(String jsonString, String... strsFieldName) {
        try {
            boolean isTrue = true;
            // 强制转换为JsonNode字段，如抛出异常则则判断不可转换
            JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString);
            Assert.notNull(jsonNode, "jsonNode为空");

            Iterator<String> iterFiledNames = jsonNode.fieldNames();
            Assert.notNull(IterUtil.isNotEmpty(iterFiledNames), "jsonNode不存在字段");

            // 遍历jsonNode字段匹配其是否存在valueType的字段中
            while (iterFiledNames.hasNext()) {
                String strFiledName = iterFiledNames.next();
                if (!ArrayUtil.contains(strsFieldName, strFiledName)) {
                    isTrue = false;
                    break;
                }
            }
            return isTrue;
        } catch (JsonProcessingException e) {
            return false;
        }
    }

    /**
     * 将JsonNode反序列化为一个Bean对象
     */
    @SneakyThrows
    public static <T> T parseObject(JsonNode jsonNode, Class<T> valueType) {
        return OBJECT_MAPPER.readValue(jsonNode.traverse(), valueType);
    }

    /**
     * 将json字符串数组反序列化为一个List对象
     */
    @SneakyThrows
    public static <T> List<T> parseArray(String jsonString, Class<T> valueType) {
        JavaType javaType = getCollectionType(ArrayList.class, valueType);
        return OBJECT_MAPPER.readValue(jsonString, javaType);
    }

    /**
     * 将JsonNode反序列化为一个List对象
     */
    @SneakyThrows
    public static <T> List<T> parseArray(JsonNode jsonNode, Class<T> valueType) {
        JavaType javaType = getCollectionType(ArrayList.class, valueType);
        return OBJECT_MAPPER.readValue(jsonNode.traverse(), javaType);
    }

    /**
     * 获取泛型的 Collection Type
     */
    public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return OBJECT_MAPPER.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

}
